#include "gamescene.h"
#include <QPainter>
#include "worldsimulator.h"
#include "polygonbody.h"
#include "edgebody.h"
#include "plane.h"
#include "wind.h"
#include "stone.h"
#include <iostream>
#include <QMouseEvent>

GameScene::GameScene(QWidget *parent)
    : QWidget(parent)
{
    kpx = 0;
    kix = 0;
    kdx = 0;
    ixItem = 0;
    lastxInput = 0;

    kpy = 0;
    kiy = 0;
    kdy = 0;
    iyItem = 0;
    lastyInput = 0;

    windForceId = -1;

    stoneGeneratePos = QPointF(-1, -1);

    frames = 0;
    setFixedSize(800, 600);
    world = new WorldSimulator;
    createEdge();
    timer = new QTimer(this);
    timer->setInterval(10);
    connect(timer, SIGNAL(timeout()), this, SLOT(step()));
    timer->start();
}

GameScene::~GameScene()
{
    delete world;
    delete timer;
}

void GameScene::setKpx(double kpx)
{
    std::cout << "setKpx kpx = " << kpx << std::endl;
    this->kpx = kpx;
}

void GameScene::setKix(double kix)
{
    std::cout << "setKix kix = " << kix << std::endl;
    this->kix = kix;
    ixItem = 0;
}

void GameScene::setKdx(double kdx)
{
    std::cout << "setKdx kdx = " << kdx << std::endl;
    this->kdx = kdx;
}

void GameScene::setKpy(double kpy)
{
    std::cout << "setKpy kpy = " << kpy << std::endl;
    this->kpy = kpy;
}

void GameScene::setKiy(double kiy)
{
    std::cout << "setKiy kiy = " << kiy << std::endl;
    this->kiy = kiy;
    iyItem = 0;
}

void GameScene::setKdy(double kdy)
{
    std::cout << "setKdy kdy = " << kdy << std::endl;
    this->kdy = kdy;
}

void GameScene::setWindOn(int hasWind)
{
    if(windForceId != -1) {
        plane->removeForce(windForceId);
        windForceId = -1;
    }

    if(hasWind) {
        ForceWrapper force(b2Vec2(rand() % 10 + 7, rand() % 10 + 5));
        windForceId = plane->applyForce(&force);
    }
}

void GameScene::createEdge()
{
    b2World *_b2World = world->getB2World();
//    EdgeBody *edge = new EdgeBody(_b2World, b2Vec2(-19.9, 0), b2Vec2(19.9, 0), b2Vec2(0, 14.9));
//    world->addObject(edge);
//    edge = new EdgeBody(_b2World, b2Vec2(-19.9, 0), b2Vec2(19.9, 0), b2Vec2(0, -14.9));
//    world->addObject(edge);
//    edge = new EdgeBody(_b2World, b2Vec2(0, -14.9), b2Vec2(0, 14.9), b2Vec2(-19.9, 0));
//    world->addObject(edge);
//    edge = new EdgeBody(_b2World, b2Vec2(0, -14.9), b2Vec2(0, 14.9), b2Vec2(19.9, 0));
//    world->addObject(edge);

    lastxInput = rand() % 32 - 16;
    lastyInput = rand() % 16 - 8;
    plane = new Plane(_b2World, b2Vec2(lastxInput, lastyInput), 0.2, 0.6, 0.2);
    world->addObject(plane);
}

void GameScene::step()
{
    ++ frames;
    if(frames % 5 == 0) {
        b2Vec2 pos = plane->getPosition();
        b2Vec2 force;
        float errorX = target.x() - pos.x;
        ixItem += kix *errorX;
        float xForceStrength = kpx * errorX + ixItem + kdx * (lastxInput - pos.x);
        lastxInput = pos.x;

        float errorY = target.y() - pos.y;
        iyItem += kiy * errorY;
        float yForceStrength = kpy * errorY + iyItem + kdy * (lastyInput - pos.y);
        lastyInput = pos.y;

//        std::cout << "force = (" << xForceStrength << ", " << yForceStrength << ")" << std::endl;
//        std::cout << "pos = (" << pos.x << "," << pos.y << ")" << std::endl;

        force.x = xForceStrength;
        force.y = yForceStrength;
        ForceWrapper f(force);
        plane->applyEngineForce(&f);

        xValues.push_back(pos.x);
        if(xValues.size() > 800) {
            xValues.pop_front();
        }

        yValues.push_back(pos.y);
        if(yValues.size() > 800) {
            yValues.pop_front();
        }
    }

    world->step();
    update();
}

void GameScene::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.fillRect(rect(), Qt::black);
    painter.setPen(Qt::darkMagenta);
    if(stoneGeneratePos.x() != -1 && stoneGeneratePos.y() != -1) {
        painter.drawLine(stoneGeneratePos, stoneDirectionPos);
    }

    painter.translate(width()/2, height()/2);

    painter.scale(scaleFactor, -scaleFactor);

    // 画出x-t时间线
    painter.save();
    painter.setPen(QPen(Qt::yellow, 1.0/scaleFactor));
    for(int i = 0; i < xValues.size(); ++ i) {
        painter.drawPoint(QPointF(i/20.0 - 20.0, xValues[i]));
    }
    painter.restore();

    painter.save();
    // 画出y-t时间线
    painter.setPen(QPen(Qt::white, 1.0/scaleFactor));
    for(int i = 0; i < yValues.size(); ++ i) {
        painter.drawPoint(QPointF(i/20.0 - 20.0, yValues[i]));
    }
    painter.restore();

    painter.setPen(QPen(Qt::white, 5.0/scaleFactor));
    // 画出目标点
    painter.drawPoint(target);

    painter.setPen(QPen(Qt::white, 1.0/scaleFactor));
    auto bodies = world->getObjects();
    for(BodyWrapper *body : bodies) {
        body->draw(&painter);
    }
}

void GameScene::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::MouseButton::LeftButton) {
        float x = (event->x() - width() / 2) / scaleFactor;
        float y = -(event->y() - height() / 2) / scaleFactor;
        target.setX(x);
        target.setY(y);
        ixItem = 0;
        iyItem = 0;
        std::cout << "new target = (" << x << "," << y << ")" << std::endl;
    } else if(event->button() == Qt::MouseButton::RightButton) {
        stoneGeneratePos = event->pos();
        stoneDirectionPos = event->pos();
    }
}

void GameScene::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::MouseButton::RightButton) {
        stoneDirectionPos = event->pos();
        QPointF deltaDistance = stoneDirectionPos - stoneGeneratePos;
        if(deltaDistance.manhattanLength() > 10) {
            float x0 = (stoneGeneratePos.x() - width() / 2) / scaleFactor;
            float y0 = -(stoneGeneratePos.y() - height() / 2) / scaleFactor;
            Stone *stone = new Stone(world->getB2World(), b2Vec2(x0, y0), 0.6, 1.0, 0.3);
            float x1 = (stoneDirectionPos.x() - width() / 2) / scaleFactor;
            float y1 = -(stoneDirectionPos.y() - height() / 2) / scaleFactor;
            stone->applyImpulse(b2Vec2((x1-x0)*5, (y1-y0)*5));
            world->addObject(stone);
        }
        stoneGeneratePos.setX(-1);
        stoneGeneratePos.setY(-1);
    }
}

void GameScene::mouseMoveEvent(QMouseEvent *event)
{
    stoneDirectionPos = event->pos();
}

const float GameScene::scaleFactor = 20.0;
